﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.Configuration;
using System.IO;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;
using System.Collections.Specialized;
using System.Xml;
using System.Xml.Linq;
using Microsoft.WindowsAzure;
using System.Net;
using Ionic.Zip;
using System.Threading.Tasks;
namespace com.dynamicdeploy.provisioningutils
{
    public class Utils
    {
        public static string LOCAL_STORAGE_NAME = "ddtempstorage";
        public const string LOCAL_ACTION_FILE_NAME = "ddactions";
        public static int HTTP_REQUEST_TIMEOUT_MILLIS = 120000;

        #region Local Storage


        public static string StorageConfigBlobLocallyGetText(string blobText, string localSubdirectoryName, string localStorageName)
        {
            string dirpath = Utils.CreateSubDirectoryInLocalStorage(localSubdirectoryName, localStorageName);
            string filePath = Path.Combine(dirpath, System.Guid.NewGuid().ToString("N") + ".cscfg");
            File.WriteAllText(filePath, blobText);

            blobText = string.Join("", File.ReadAllLines(filePath));

            Directory.Delete(dirpath, true);

            return blobText;

        }



        public static string StoreBlobLocallyText(string blobText, string fileName)
        {

            string path = Path.Combine(GetLocalStorageRootPath(LOCAL_STORAGE_NAME), fileName);
            if (!File.Exists(path))
            {
                File.WriteAllText(path, blobText);
            }

            return path;

        }

        public static string StoreBlobLocallyText(string localStorageName, string blobText, string fileName)
        {

            string path = Path.Combine(GetLocalStorageRootPath(localStorageName), fileName);
            if (!File.Exists(path))
            {
                File.WriteAllText(path, blobText);
            }

            return path;

        }

        public static string StoreBlobLocally(byte[] blob, string fileName)
        {

            string path = Path.Combine(GetLocalStorageRootPath(LOCAL_STORAGE_NAME), fileName);
            if (!File.Exists(path))
            {
                File.WriteAllBytes(path, blob);
            }
            return path;

        }

        public static string StoreBlobLocally(CloudBlob blob, string fileName)
        {

            string path = Path.Combine(GetLocalStorageRootPath(LOCAL_STORAGE_NAME), fileName);

            if (!File.Exists(path))
            {

                blob.DownloadToFile(path);

            }

            return path;

        }
        public static LocalResource GetLocalStorageResource(string localStorageName)
        {
            try
            {
                LocalResource resource = RoleEnvironment.GetLocalResource(localStorageName);

                return resource;

            }
            catch (Exception ex)
            {

                throw ex;
            }



        }

        public static string GetLocalStorageRootPath(string localStorageName)
        {
            try
            {
                if (RoleEnvironment.IsAvailable)
                {
                    LocalResource resource = RoleEnvironment.GetLocalResource(localStorageName);

                    return resource.RootPath;
                }
                else
                {
                    string path = Path.Combine(Environment.CurrentDirectory, localStorageName);
                    if (!Directory.Exists(path))
                    {
                        return Directory.CreateDirectory(path).FullName;
                    }

                    return path;
                }

            }
            catch (Exception ex)
            {


                throw ex;
            }



        }

        public static bool CanAccessLocalStorage(string localStorageName)
        {

            bool ret = false;
            try
            {
                string fp = GetLocalStorageRootPath(localStorageName) + "proazure.txt";

                using (StreamWriter sw = File.CreateText(fp))
                {

                    sw.WriteLine("This is a Pro Azure file.");



                }//using

                string fpNew = GetLocalStorageRootPath(localStorageName) + "proazure2.txt";
                File.Copy(fp, fpNew);
                string fpNew2 = GetLocalStorageRootPath(localStorageName) + "proazure3.txt";
                File.Move(fp, fpNew2);

                File.Delete(fpNew2);



                File.Delete(fpNew);


                ret = true;

            }
            catch (Exception)
            {



            }

            return ret;
        }



        public static string CreateSubDirectoryInLocalStorage(string dirName, string localStorageName)
        {

            string path = GetLocalStorageRootPath(localStorageName);
            path = Path.Combine(path, dirName);


            if (!Directory.Exists(path))
            {
                return Directory.CreateDirectory(path).FullName;
            }



            return path;
        }


        public static string WriteTextToLocalStorage(string fileName, string localStorageName, string message, Encoding enc)
        {

            string path = GetLocalStorageRootPath(localStorageName);
            path = Path.Combine(path, fileName);


            if (enc == null) enc = Encoding.UTF8;

            File.WriteAllText(path, message, enc);

            return path;
        }

        public static string WriteBytesToLocalStorage(string fileName, string localStorageName, byte[] message, Encoding enc)
        {

            string path = GetLocalStorageRootPath(localStorageName);
            path = Path.Combine(path, fileName);

            File.WriteAllBytes(path, message);

            return path;

        }

        public static string ReadTextFromLocalStorage(string fileName, string localStorageName)
        {

            string path = Path.Combine(GetLocalStorageRootPath(localStorageName), fileName);


            if (File.Exists(path))
            {
                return string.Join("", File.ReadAllLines(path));
            }

            return "";
        }
        public static IList<string> ReadAllLinesFromLocalStorage(string fileName, string localStorageName)
        {
            List<string> messages = new List<string>();
            string path = Path.Combine(GetLocalStorageRootPath(localStorageName), fileName);


            if (File.Exists(path))
            {
                using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    StreamReader reader = new StreamReader(stream, Encoding.UTF8);

                    while (true)
                    {
                        string line = reader.ReadLine();
                        if (line == null) break;
                        messages.Add(line);
                    }
                }
            }

            return messages;
        }




        #endregion

        #region Xml 

    

        #region GetSharedAccess
        public static Uri GetShareAccessUriFromStorageAccountSettingName(string storageAccountSettingName , string blobName, string containerName = null, int expiryMinutes=10)

        {
            CloudStorageAccount storageAccount = null;

             try
            {
                storageAccount = String.IsNullOrEmpty(storageAccountSettingName) ? null : CloudStorageAccount.FromConfigurationSetting(storageAccountSettingName);

                 return GetSharedAccessUri(storageAccount, blobName, containerName, expiryMinutes);
            }
            catch (Exception ex)
            {
                throw new ArgumentException("The Azure storage connection string is not valid:  " + ex.Message, storageAccountSettingName);
            }

        }

        public static Uri GetShareAccessUriFromStorageAccountConnectionString(string storageAccountConnectionString , string blobName, string containerName = null, int expiryMinutes=10)

        {
            CloudStorageAccount storageAccount = null;

             try
            {
                storageAccount = String.IsNullOrEmpty(storageAccountConnectionString) ? null : CloudStorageAccount.Parse(storageAccountConnectionString);

                return GetSharedAccessUri(storageAccount, blobName, containerName, expiryMinutes);
            }
            catch (Exception ex)
            {
                throw new ArgumentException("The Azure storage connection string is not valid:  " + ex.Message, storageAccountConnectionString);
            }

        }

        public static Uri GetSharedAccessUri(CloudStorageAccount storageAccount, string blobName, string containerName = null, int expiryMinutes=10)
        {
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
           

            if (!string.IsNullOrEmpty(containerName))
            {
                blobName = string.Format("{0}/{1}", containerName, blobName);

            }
            var reference = blobClient.GetBlobReference(blobName.StartsWith("/") ? blobName.Substring(1) : blobName);
            reference.FetchAttributes();

            string m = System.Web.HttpUtility.UrlDecode(reference.Metadata["expiration_time"]);

            if (!string.IsNullOrEmpty(m))
            {
                DateTime dt = DateTime.Parse(m);

                if (DateTime.UtcNow <= dt)
                {
                    var sas = reference.GetSharedAccessSignature(
               new SharedAccessPolicy()
               {
                   Permissions = SharedAccessPermissions.Read,
                   SharedAccessExpiryTime = DateTime.Now.AddMinutes(expiryMinutes)
               });

                    return new Uri(reference.Uri, sas);
                }

            }

            return null;
        }

        #endregion

        #region Download Blob
        public static string GetBlobLocally(CloudStorageAccount storageClient, string localStoragePath, string containerName, string blobName)
        {
            if (storageClient != null)
            {
                //Get the file
                CloudBlobClient blobClient = storageClient.CreateCloudBlobClient();
                CloudBlobContainer container = blobClient.GetContainerReference(containerName);

                CloudBlob b = container.GetBlobReference(blobName);
                if (b != null )
                {
                    b.FetchAttributes();

                    string fileName = blobName;
                    string fPath = Path.Combine(localStoragePath, fileName);
                    b.DownloadToFile(fPath);
                    return fPath;

                }

            }

            return null;
        }



        public static string GetBlobLocally(string fileUrl, string localStoragePath, string fileName)
        {
            Uri urlToDownload = null;
            if (!Uri.TryCreate(fileUrl, UriKind.Absolute, out urlToDownload))
                throw new ArgumentException("Invalid Uri", fileUrl);

            return GetBlobLocally(urlToDownload, localStoragePath, fileName);
        }
        public static string GetBlobLocally(Uri urlToDownload, string localStoragePath, string fileName)
        {
            if (urlToDownload != null)
            {
                if (string.IsNullOrEmpty(fileName))
                {
                    fileName = GetFileNameFromUrl(urlToDownload.AbsoluteUri);
                }
                string fPath = Path.Combine(localStoragePath, fileName);

                //using (var client = new WebClient())
                //{
                //    client.DownloadFile(urlToDownload, fPath);
                //}

                HttpWebRequest req = HttpWebRequest.Create(urlToDownload) as HttpWebRequest;
                // *** Set properties
                req.Timeout = HTTP_REQUEST_TIMEOUT_MILLIS;
              
                using (HttpWebResponse response = req.GetResponse() as HttpWebResponse)
                {

                    if (response != null)
                    {

                        // Get the stream.
                        byte[] data = null;
                        using (Stream stream = response.GetResponseStream())
                        {
                            // Use the ReadFully method from the link above:
                            data = ReadFully(stream, (int)response.ContentLength);

                            // Return the memory stream.

                        }

                        if (data != null && data.Length > 0)
                        {



                            File.WriteAllBytes(fPath, data);


                        }
                    }
                }
                return fPath;



            }

            return null;
        }
        public static void ExtractFiles(string localFolder, string zipFile)
        {

            using (ZipFile zip1 = ZipFile.Read(Path.Combine(localFolder, zipFile)))
            {

                foreach (ZipEntry e in zip1)
                {
                    e.Extract(localFolder, ExtractExistingFileAction.OverwriteSilently);
                }
            }
        }

        public static string GetFileNameFromUrl(string url)
        {
            return url.Substring(url.LastIndexOf("/") + 1, (url.Length - url.LastIndexOf("/") - 1));


        }
        public static string DownloadFileWithHttp(string fileUrl, string localFolder, int timeoutInMillis = 120000, bool extract = false)
        {
             
             Uri uri = new Uri(fileUrl);

             string fileName = GetFileNameFromUrl(fileUrl);

             if (!string.IsNullOrEmpty(fileName))
             {
                 string filePath = Path.Combine(localFolder, fileName);

                 if (!File.Exists(filePath))
                 {
                     HttpWebRequest request = HttpWebRequest.Create(fileUrl) as HttpWebRequest;
                     // *** Set properties
                     request.Timeout = timeoutInMillis;
                     using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                     {
                         // Get the stream.
                         byte[] data = null;
                         using (Stream stream = response.GetResponseStream())
                         {
                             // Use the ReadFully method from the link above:
                             data = ReadFully(stream, (int)response.ContentLength);

                             // Return the memory stream.

                         }

                         if (data != null && data.Length > 0)
                         {



                             File.WriteAllBytes(filePath, data);

                          
                         }


                     }
                 }

                 if (extract)
                 {

                     ExtractFiles(localFolder, fileName);
                 }

                 return filePath;

                

             }

            return null;
            //using (HttpWebResponse response = req.GetResponse() as HttpWebResponse)
            //{
            //    byte[] arrBuffer = new byte[0];
            //    using (BinaryReader reader = new
            //    BinaryReader(response.GetResponseStream()))
            //    {
            //        byte[] arrScratch = null;
            //        while ((arrScratch = reader.ReadBytes(4096)).Length > 0)
            //        {
            //            if (arrBuffer.Length == 0)
            //                arrBuffer = arrScratch;
            //            else
            //            {
            //                byte[] arrTemp = new byte[arrBuffer.Length + arrScratch.Length];
            //                Array.Copy(arrBuffer, arrTemp, arrBuffer.Length);
            //                Array.Copy(arrScratch, 0, arrTemp, arrBuffer.Length, arrScratch.Length);
            //                arrBuffer = arrTemp;
            //            }
            //        }
            //    }

            //}
            // use arrBuffer now...

        }
        /// <summary>
        /// Reads data from a stream until the end is reached. The
        /// data is returned as a byte array. An IOException is
        /// thrown if any of the underlying IO calls fail.
        /// </summary>
        /// <param name="stream">The stream to read data from</param>
        /// <param name="initialLength">The initial buffer length</param>
        public static byte[] ReadFully(Stream stream, int initialLength)
        {
            // If we've been passed an unhelpful initial length, just
            // use 32K.
            if (initialLength < 1)
            {
                initialLength = 32768;
            }

            byte[] buffer = new byte[initialLength];
            int read = 0;

            int chunk;
            while ((chunk = stream.Read(buffer, read, (buffer.Length - read))) > 0)
            {
                read += chunk;

                // If we've reached the end of our buffer, check to see if there's
                // any more information
                if (read == buffer.Length)
                {
                    int nextByte = stream.ReadByte();

                    // End of stream? If so, we're done
                    if (nextByte == -1)
                    {
                        return buffer;
                    }

                    // Nope. Resize the buffer, put in the byte we've just
                    // read, and continue
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Array.Copy(buffer, newBuffer, buffer.Length);
                    newBuffer[read] = (byte)nextByte;
                    buffer = newBuffer;
                    read++;
                }
            }
            // Buffer is now too big. Shrink it.
            byte[] ret = new byte[read];
            Array.Copy(buffer, ret, read);
            return ret;
        }

        #endregion
        #endregion

        #region Configuration



        /// <summary>
        /// Gets the value from the config value from ServiceConfiguration if it exists otherwise gets it from web.config or app.config
        /// </summary>
        /// <param name="configName"></param>
        /// <returns></returns>
        public static string GetConfigValueAsString(string configName)
        {
            return (RoleEnvironment.IsAvailable)
                                   ? RoleEnvironment.GetConfigurationSettingValue(configName)
                                   : ConfigurationManager.AppSettings[configName];

        }

        public static string GetStringConfigurationValue(string configName)
        {

            try
            {

                return GetConfigValueAsString(configName);


            }
            catch (Exception ex)
            {
              

                throw ex;

            }


        }

        public static bool GetBooleanConfigurationValue(string configName)
        {

            try
            {
                bool ret;
                if (bool.TryParse(GetConfigValueAsString(configName), out ret))
                {
                    return ret;
                }
                else
                {
                  

                    throw new Exception(String.Format("Could not parse value for configuration setting {0}", configName));


                }


            }
            catch (Exception ex)
            {
               

                throw ex;

            }


        }

        public static int GetIntConfigurationValue(string configName)
        {

            try
            {
                int ret;
                if (int.TryParse(GetConfigValueAsString(configName), out ret))
                {
                    return ret;
                }
                else
                {
                  

                    throw new Exception(String.Format("Could not parse value for configuration setting {0}", configName));


                }


            }
            catch (Exception ex)
            {
              

                throw ex;

            }


        }

        public static double GetDoubleConfigurationValue(string configName)
        {

            try
            {
                double ret;
                if (double.TryParse(GetConfigValueAsString(configName), out ret))
                {
                    return ret;
                }
                else
                {
                   

                    throw new Exception(String.Format("Could not parse value for configuration setting {0}", configName));


                }


            }
            catch (Exception ex)
            {
                

                throw ex;

            }


        }

        public static string GetLogLevel()
        {
            try
            {
                return GetConfigValueAsString("LogLevel").ToLower();
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message, "Error");

            }

            return "Info";
        }

        #endregion

        #region Upload Blob Recursively
        public static void UploadDirectoryRecursive(string path, CloudBlobContainer container)
        {

            // use 16 threads to upload
            Parallel.ForEach(EnumerateDirectoryRecursive(path),
                new ParallelOptions { MaxDegreeOfParallelism = 16 },
                (file) =>
                {
                    string blobName = Path.GetFullPath(file).Substring(path.Length);
                    if (blobName.StartsWith("/") || blobName.StartsWith(@"\"))
                    {
                        blobName = blobName.Substring(1);
                    }
                    // upload each file, using the relative path as a blob name
                    UploadFile(file, container.GetBlobReference(blobName));

                });

       
        }

        private static IEnumerable<string> EnumerateDirectoryRecursive(string root)
        {
            foreach (var file in Directory.GetFiles(root))
                yield return file;
            foreach (var subdir in Directory.GetDirectories(root))
                foreach (var file in EnumerateDirectoryRecursive(subdir))
                    yield return file;
        }

        private static void UploadFile(string filename, CloudBlob blob)
        {
            blob.UploadFile(filename);
        }
        #endregion
    }
}
